<?php
    require_once '../data/Row.php';
    
    /**
     *	DataSet class
     *	Provides a wrapper around database data and holds relevant metadata.
     *	Tied to a specific database table.
     *	@author	Simon Copsey <simon.copsey@gmail.com>
     *	@version 0.21
     *	@package data
     */
    class DataSet {
        private $tableName;
        private $identifierFieldName;
        private $identifierFieldIndex = -1;
        private $columnNames = array();
        private $columnCount = 0;
        private $rowData = array();
        private $rowCount = 0;
        
        /**
        * Creates a new DataSet linked to the given table.
        * Each row will be indexed against the given unique identifier field
        * in case of future updates.
        *
        * @param String $aTableName name of table from which to retrieve data
        * @param String $anIdentifierField name of (unique) identifier field
        * @param String[] $aColumnNameSet array containing ordered table column names
        * @access public
        */
        public function __construct($aTableName, $anIdentifierFieldName, $aColumnNameSet) {
            $this->tableName = $aTableName;
            $this->identifierFieldName = trim($anIdentifierFieldName);
            // gather column metadata
            $this->columnCount = count($aColumnNameSet);
            for ($currentColumn = 0; $currentColumn < $this->columnCount; $currentColumn++) {
                if (trim($aColumnNameSet[$currentColumn]) == $anIdentifierFieldName) {
                    $this->identifierFieldIndex = $currentColumn;
                }
                array_push($this->columnNames, $aColumnNameSet[$currentColumn]);
            }
        }
        
        /**
        * Populates DataSet with contents of a ResultSet returned by a database query.
        * Data must be from the same table and contain the same number of columns
        * as used when constructing the DataSet instance.
        *
        * @param ResultSet $aResultSet ResultSet from which to populate DataSet
        * @return boolean true if successful, else false
        * @access private
        */
        public function populate($aResultSet) {
            if ($aResultSet == null) {
                return false;
            }
            // perform column count check
            $data = $aResultSet->fetch_array(MYSQL_NUM);
            if (count($data) != $this->columnCount) {
                return false;
            }
            
            // gather row data
            $this->rowData = array();
            do {
                $row = new Row();
                $row->populate($data);
                array_push($this->rowData, $row);
            } while  ($data = $aResultSet->fetch_array());            
            $this->rowCount = count($this->rowData);
            return true;
        }
        
        /**
        * Returns the row relating to the specified row index.
        *
        * @param Integer $anIndex row index of which to return data
        * @return String[] row data if index valid, else empty array
        * @access public
        */        
        public function getRow($anIndex) {
            if ($anIndex > count($this->rowData) || $anIndex < 0) {
                return array();
            }
            return $this->rowData[$anIndex];
        }
        
        /**
        * Add a new, empty row.
        * Row will be inserted at end of array (and given the highest index).
        *
        * @param Row $aRow row to be added
        * @return boolean true if successful, else false
        * @access public
        */   
        public function addRow($aRow) {
            array_push($this->rowData, $aRow);
            $this->rowCount++;
            return true;
        }
        
        
        /**
        * Delete the entire row with the specified index.
        *
        * @param Integer $aRowIndex row index of which to delete
        * @return boolean true if successful, else false
        * @access public
        */     
        public function deleteRow($anIndex) {
            // perform bound check
            if ($anIndex > $this->rowCount) {
                return false;
            }
            // perform deletion
            if ($this->rowData[$anIndex]->isNew()) {
                // row not in database, so can simply discard
                $this->rowData = array_splice($this->rowData, $anIndex, 1);
            } else {
                // row in database, so mark for deletion upon later commit
                $this->rowData[$anIndex]->setDeleted(true);
            }
            $this->rowCount--;
            return true;
        }
        
        
        /**
        * Sets the values for the entire row relating to the specified index.
        *
        * @param Integer $aRowIndex row index of which to update data
        * @param Row $aRowData new data for row
        * @return boolean true if successful, else false
        * @access public
        */     
        public function updateRow($aRowIndex, $aRowData) {
            // perform bound and column count check
            if ($aRowIndex > $this->rowCount || count($aRowData->getValues()) != $this->columnCount) {
                return false;
            }
            // perform update
            $this->rowData[$aRowIndex] = $aRowData;
            return true;
        }
        
        /**
        * Returns individual value within the specified row and column.
        *
        * @param Integer $aRowIndex row index of which to return data
        * @param Integer $aColumnIndex column index of which to return data
        * @return String data value if valid, else null
        * @access public
        */        
        public function getValue($aRowIndex, $aColumnIndex) {
            $row = $this->getRow($aRowIndex);
            // perform bound check
            if ($row == null || $aColumnIndex > count($row)) {
                return null;
            }
            return $row->getValue($aColumnIndex);
        }
        
        /**
        * Sets individual value within specified row and column.
        *
        * @param Integer $aRowIndex row index of which to return data
        * @param Integer $aColumnIndex column index of which to return data
        * @param String $aValue value of which to set
        * @return true if successful, else false
        * @access public
        */     
        public function updateValue($aRowIndex, $aColumnIndex, $aValue) {
            // perform bound check
            if ($aRowIndex > $this->rowCount || $aColumnIndex > $this->columnCount) {
                return false;
            }
            // perform update
            $this->rowData[$aRowIndex]->updateValue($aColumnIndex, $aValue);
            return true;
        }
        
        /**
        * Returns the column name relating to the specified index.
        *
        * @param Integer $anIndex column index of which to return name
        * @return String column name if index valid, else null
        * @access public
        */        
        public function getColumnName($anIndex) {
            if ($anIndex > $this->columnCount) {
                return null;
            }
            return $this->columnNames[$anIndex];
        }
        
        /**
        * Returns all column names.
        *
        * @return String[] column names if present, else empty array
        * @access public
        */
        public function getColumnNames() {
            if ($this->columnCount < 0) {
                return array();
            }
            return $this->columnNames;
        }
        
        /**
        * Returns total number of columns.
        *
        * @return Integer number of columns
        * @access public
        */                
        public function getColumnCount() {
            return $this->columnCount;
        }
        
        /**
        * Returns total number of rows.
        *
        * @return Integer number of rows
        * @access public
        */
        public function getRowCount() {
            return $this->rowCount;
        }
        
        /**
        * Returns name of table from which to retrieve data.
        *
        * @return String name of table
        * @access public
        */
        public function getTableName() {
            return $this->tableName;
        }
        
        /**
        * Returns identifier field name.
        *
        * @return String name of (unique) identifier field
        * @access public
        */
        public function getIdentifierFieldName() {
            return $this->identifierFieldName;
        }
        
        /**
        * Returns identifier field index.
        *
        * @return Integer index of (unique) identifier field if successful, else -1
        * @access public
        */
        public function getIdentifierFieldIndex() {
            return $this->identifierFieldIndex;
        }
    }
?>